package com.ld.common.aop;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.ld.shieldsb.annotation.duplicate.SubmitRequestException;
import com.ld.shieldsb.annotation.duplicate.SubmitRequestIntercept;
import com.ld.shieldsb.common.web.tag.model.TokenModel;
import com.ld.shieldsb.common.web.util.Web;

import lombok.extern.slf4j.Slf4j;

/**
 * 拦截表单重复提交<br>
 * <ul>
 * <li>session中判断同一请求（访问同一方法）的时间间隔</li>
 * <li>表单中增加token每次验证s</li>
 * </ul>
 * 
 * @ClassName DuplicateSubmitAspect
 * @author <a href="mailto:donggongai@126.com" target="_blank">吕凯</a>
 * @date 2019年7月5日 上午8:50:22
 *
 */
@Aspect
@Component
@Slf4j
public class DuplicateSubmitAspect extends BasicAop {
    public static final String DUPLICATE_TOKEN_KEY = "duplicate_token_key";

    /**
     * 定义切入点
     */
//    @Pointcut("@annotation(token)")
//    public void doPointCut(DuplicateRequestIntercept token) {
//
//    }

    /**
     * 针对注解的切入有2中写法，下面用的@annotation(形参)，形参必须在方法参数中存在。<br>
     * // 指定切面<br>
     * @Pointcut("@annotation(com.great.annotation.TestAnnotation)")<br>
     * public void annotationPointCut() {<br>
     * 
     * }<br>
     * // @Before可以有两者写法, @annotation(函数名annotationPointCut)<br>
     * @Around("annotationPointCut()")<br>
     */
    @Around("@annotation(token)")
    public Object around(ProceedingJoinPoint pjp, SubmitRequestIntercept token) {
        outLog("方法环绕start.....");
        String msg = "提交太频繁，请稍候再试！";
        try {
            if (token != null) {
                ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                HttpServletRequest request = attributes.getRequest();
                HttpSession session = request.getSession();
//                HttpServletResponse response = attributes.getResponse();
                Boolean checkCsrf = token.csrf();
                if (checkCsrf) { // 使用csfr验证，可以通过注解参数关闭
                    TokenModel csrfToken = (TokenModel) session.getAttribute(Web.Attr.SESSION_TOKEN_KEY); //
                    if (csrfToken == null) {
                        return Web.Response.error("参数无效，请刷新(" + Web.Attr.ERROR_PARAMSTER_VALID_NONE + ")！");
                    } else {
                        String paramterName = csrfToken.getParameterName();
                        String tokenVal = request.getParameter(paramterName);
                        if (tokenVal == null) {
                            tokenVal = request.getHeader(paramterName);
                        }
                        if (!csrfToken.getToken().equals(tokenVal)) {
                            return Web.Response.error("参数无效(" + Web.Attr.ERROR_PARAMSTER_VALID_UNEQUAL + ")！");
                        }
                    }
                }
                boolean tooFrequent = false;
                if (token.frequency()) { // 进行频率验证
                    String key = getDuplicateTokenKey(pjp); // 不同方法设置不同的规则
                    Long t = (Long) session.getAttribute(key);
                    long timeMillis = System.currentTimeMillis();
                    // log.warn("" + timeMillis + " " + t + " " + (timeMillis - t) + " " + token.interval() * 1000);
                    if (null == t || (timeMillis - t) > token.interval() * 1000) { // 没有提交过，或者已经超过间隔时间，则可以提交，否则不能提交
                        session.setAttribute(key, timeMillis);
                        log.info("token-key=" + key);
                        log.info("token-value=" + timeMillis + "");
                    } else if (null != t) {
                        tooFrequent = true;
                        log.warn("2次提交不能少于" + token.interval() + "秒（" + (timeMillis - t) + "ms）");
                    }
                }
                if (!tooFrequent) { // 不验证的直接放行
                    Object o = pjp.proceed();
                    outLog("方法环绕proceed，结果是 :" + o);
                    return o;
                }

            }
            return Web.Response.error(msg);
        } catch (Throwable e) {
            e.printStackTrace();
            return null;
        }
    }

    @Before("@annotation(token)")
    public void before(final JoinPoint joinPoint, SubmitRequestIntercept token) throws SubmitRequestException {

    }

    /**
     * 获取重复提交的key
     * 
     * @param joinPoint
     * @return
     */
    public String getDuplicateTokenKey(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        String declareTypeName = signature.getDeclaringTypeName();
        String methodName = signature.getName();
        StringBuilder key = new StringBuilder(DUPLICATE_TOKEN_KEY);
        key.append(",").append(declareTypeName).append(methodName);
        return key.toString();
    }

    /*    @AfterReturning("@annotation(token)")
    public void doAfterReturning(JoinPoint joinPoint, SubmitRequestIntercept token) {
        // 处理完请求，返回内容
        log.info("出方法：");
        if (token != null) {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            boolean isSaveSession = token.save();
            if (isSaveSession) {
                String key = getDuplicateTokenKey(joinPoint);
                Object t = request.getSession().getAttribute(key);
    //                if (null != t && token.type() == SubmitRequestIntercept.REQUEST) {
    //                request.getSession(false).removeAttribute(key);
    //                }
            }
        }
    }*/

    /**
     * 异常
     * 
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(pointcut = "@annotation(token)", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable e, SubmitRequestIntercept token) {
        // 处理处理重复提交本身之外的异常
        if (null != token && e instanceof SubmitRequestException == false) {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
//            boolean isSaveSession = token.save();
            // 获得方法名称
//            if (isSaveSession) {
            String key = getDuplicateTokenKey(joinPoint);
            Object t = request.getSession().getAttribute(key);
            if (null != t) {
                // 方法执行完毕移除请求重复标记
                request.getSession(false).removeAttribute(key);
                log.info("异常情况--移除标记！");
            }
//            }
        }
    }
}
